package com.ler.auth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Leron
 * @version 1.0
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    private TokenStore tokenStore;

    @Resource
    private ClientDetailsService custumeclientDetailsService;

    /**
     * 认证管理器
     */
    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Resource
    private AuthorizationCodeServices authorizationCodeServices;

    @Resource
    private JwtTokenEnhancer jwtTokenEnhancer;

    @Resource
    private JwtAccessTokenConverter accessTokenConverter;

    /**
     * 配置令牌管理服务
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        // 配置客户端详情服务
        services.setClientDetailsService(custumeclientDetailsService);
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore);

        // 使用jwt令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        services.setTokenEnhancer(tokenEnhancerChain);
        // 令牌有效期 (2h)
        services.setAccessTokenValiditySeconds(7200);
        // 刷新令牌有效期 (3d)
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(custumeclientDetailsService);
    }

    /**
     * 配置 token 访问端点和令牌服务
     * @param endpoints the endpoints configurer
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(accessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器
        endpoints.authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices)
                .accessTokenConverter(accessTokenConverter)
                .tokenEnhancer(enhancerChain);
    }

    /**
     * 配置令牌端点的安全约束
     * @param security a fluent configurer for security features
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 公开 token_key 路径 /oauth/token_key
                .tokenKeyAccess("permitAll()")
                // 公开 /oauth/check_token 解析和校验令牌的合法性
                .checkTokenAccess("permitAll()")
                // 允许表单认证申请令牌
                .allowFormAuthenticationForClients();
    }
}
